package com.hgsoft.pubsv.component.apifilter.init;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import com.hgsoft.pubsv.component.apifilter.SecurityParam;

/**
 * api安全注解上下文
 * @author saint
 *
 */
public class ApiSecuritData implements ApplicationContextAware {
	
	private Logger logger = LoggerFactory.getLogger(ApiSecuritData.class);
	
	public static Map<String, SecurityParam> requestSecurityParamMap = new HashMap<>();

	private String contextPath;
	
    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
    	this.contextPath = ctx.getEnvironment().getProperty("server.servlet.context-path");
		Map<String, Object> beanMap = ctx.getBeansWithAnnotation(Controller.class);
        initData(beanMap);
    }

	private void initData(Map<String, Object> beanMap) {
		if (beanMap != null) {
            for (Object bean : beanMap.values()) {
                Class<?> clz = bean.getClass();
                Method[] methods = clz.getMethods();
                for (Method method : methods) {
                	SecurityParam securityParam = AnnotationUtils.findAnnotation(method, SecurityParam.class);
                	if (securityParam != null) {
                		// 注解中的URI优先级高
                    	String uri = securityParam.value();
                    	if (!StringUtils.hasText(uri)) {
                    		Set<String> uriSet = getApiUriSet(clz, method);
                    		for (String url : uriSet) {
                    			requestSecurityParamMap.put(url, securityParam);
                    			logger.info("Securit URI: {}", url);
							}
						}else {
							requestSecurityParamMap.put(uri, securityParam);
	                        logger.info("Securit URI: {}", uri);
						}
                	}
                }
            }
        }
	}
	
	private Set<String> getApiUriSet(Class<?> clz, Method method) {
		RequestMapping reqMapping = AnnotationUtils.findAnnotation(clz, RequestMapping.class);
		Set<String> set = new HashSet<>();
		if (reqMapping == null) {
			set.add(getApiUri(clz, method,""));
		}else {
			for (String prefix : reqMapping.value()) {
				set.add(getApiUri(clz, method,prefix));
			}
		}
		return set;
	}
    
	private String getApiUri(Class<?> clz, Method method,String prefix) {
    	String methodType = "";
        StringBuilder uri = new StringBuilder();
        
        if (!StringUtils.isEmpty(prefix)) {
        	uri.append(formatUri(prefix));
		}
        
        GetMapping getMapping = AnnotationUtils.findAnnotation(method, GetMapping.class);
        PostMapping postMapping = AnnotationUtils.findAnnotation(method, PostMapping.class);
        RequestMapping requestMapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
        PutMapping putMapping = AnnotationUtils.findAnnotation(method, PutMapping.class);
        DeleteMapping deleteMapping = AnnotationUtils.findAnnotation(method, DeleteMapping.class);
        
        if (getMapping != null) {
        	methodType = HttpMethod.GET.name();
            uri.append(formatUri(getMapping.value()[0]));
            
        } else if (postMapping != null) {
        	methodType = HttpMethod.POST.name();
            uri.append(formatUri(postMapping.value()[0]));
            
        } else if (putMapping != null) {
        	methodType = HttpMethod.PUT.name();
            uri.append(formatUri(putMapping.value()[0]));
            
        } else if (deleteMapping != null) {
        	methodType = HttpMethod.DELETE.name();
            uri.append(formatUri(deleteMapping.value()[0]));
            
        } else if (requestMapping != null) {
        	//默认post提交 TODO可能导致 漏加解密，推荐指定一种请求方式
        	if(requestMapping.method().length>0) {
        		RequestMethod m = requestMapping.method()[0];
            	methodType = m.name();
        	}else {
        		methodType = HttpMethod.POST.name();
        	}
            uri.append(formatUri(requestMapping.value()[0]));
        } 
        
        if (StringUtils.hasText(this.contextPath) && !"/".equals(this.contextPath)) {
        	 return methodType.toLowerCase() + ":" + this.contextPath + uri.toString();
		}
        
        return methodType.toLowerCase() + ":" + uri.toString();
	}
    
    private String formatUri(String uri) {
    	if (uri.startsWith("/")) {
			return uri;
		}
    	return "/" + uri;
    }
}
